<template>
  <div class="w-full h-full relative overflow-auto" ref="karaokeRef">
    <div class="pla-top"></div>
    <div class="scale-line-wrap" v-if="scaleLineShow">
      <slot name="scale-line" :scaleLineCurrentTime="scaleLineCurrentTime">
        <div class="w-full scale-line"></div>
        <div class="absolute right-0 top-0 text-[#9ca3af]">{{ scaleLineCurrentTimeString }}</div>
        <v-btn variant="text" class="absolute left-0 top-0" @click="handleClick">播放</v-btn>
      </slot>
    </div>

    <KaraokeLine
      v-for="(lyricLine, index) in lyrics"
      :key="index"
      :lyrics="lyricLine"
      :current-time="currentTime"
      :font-size="fontSize"
      :active-font-size="activeFontSize"
      :actived="currentIndex === index"
      :char-spacing="charSpacing"
      :base-color="baseColor"
      :highlight-color="highlightColor"
      :line-height="lineHeight"
      :highlight-shadow="highlightShadow"
      :font-weight="fontWeight"
      :active-color="activeColor"
      :ref="(line:any)=>setKaraokeLineHeight(index,line?.height)"
    />
    <div class="pla-bot"></div>
  </div>
</template>
<script setup lang="ts">
import { Runner, Lrc } from "lrc-kit";
import { defaultProps, delyScaleLineShowTime } from "../default";
import KaraokeLine from "../karaoke-line/index.vue";
import { parseAss, parseAss2Lrc, parseLrc, timestampToTimeString } from "../../utils";
import { toRefs, computed, ref, watch, useTemplateRef } from "vue";
import { KaraokeProps } from "@/package/ass-parse/type";
import { useScroll, useElementBounding, useDebounceFn } from "@vueuse/core";
const props = withDefaults(defineProps<KaraokeProps>(), defaultProps);
const emit = defineEmits(["scaleLineClick"]);
const runner = new Runner();
const currentIndex = ref(-1);
const updateAutoActionScrolled = ref(false);
// 延迟显示刻度
const delyScaleLineShow = ref(false);
const karaokeRef = useTemplateRef("karaokeRef");
const { assLyric, currentTime, activeFontSize, fontSize, charSpacing, lrcLyric, lyric } = toRefs(props);
const karaokeLineHeight = ref<number[]>([]);
const { height, top, width, left } = useElementBounding(karaokeRef, {
  updateTiming: "next-frame",
});
const scaleLineWidth = computed(() => width.value + "px");
const scaleLineLeft = computed(() => left.value + "px");
const lyrics = computed(() => {
  if (assLyric.value) {
    return parseAss(assLyric.value);
  }
  if (lrcLyric.value) {
    return parseLrc(lrcLyric.value)?.lyrics ?? [];
  }
  if (lyric.value) {
    return lyric.value?.split("\n");
  }
  return [];
});
const lrc = computed(() => {
  if (assLyric.value) {
    return parseAss2Lrc(assLyric.value);
  }
  if (lrcLyric.value) {
    return lrcLyric.value;
  }
  return null;
});
const lrcParse = computed(() => {
  if (lrc.value) {
    return parseLrc(lrc.value);
  }
  return null;
});

const scaleLineShow = computed(() => {
  if (!lrcParse.value) return false;
  if (delyScaleLineShow.value) return true;
  return !updateAutoActionScrolled.value && isScrolling.value;
});
const scaleLineWrapTop = computed(() => {
  return height.value / 2 + karaokeLineHeight.value[0] / 2 + "px";
});
const scaleLineCurrentTime = computed(() => {
  const scrollTop2Index = getScrollTop2Index();
  if (scrollTop2Index !== undefined && lrcParse.value?.lyrics) {
    const timestamp = lrcParse.value?.lyrics[scrollTop2Index]?.timestamp;
    if (timestamp === undefined) return null;
    return timestamp;
  }
  return null;
});
const scaleLineCurrentTimeString = computed(() => {
  if (scaleLineCurrentTime.value == null) {
    return "";
  }
  return timestampToTimeString(scaleLineCurrentTime.value);
});

const { y, isScrolling } = useScroll(karaokeRef, {
  behavior: "smooth",
  onStop: onScrollStop,
});

const setKaraokeLineHeight = (index: number, height: number) => {
  karaokeLineHeight.value[index] = height;
};
const updateCurrentIndex = () => {
  runner.timeUpdate(currentTime.value);
  currentIndex.value = runner.curIndex();
};
const updateScrollTop = (index: number) => {
  if (index >= karaokeLineHeight.value.length) return false;
  const height = karaokeLineHeight.value.reduce((pre, cur, currentIndex) => {
    if (currentIndex < index) {
      return pre + cur;
    }
    return pre;
  }, 0);
  if (!height) return false;
  if (y.value != height) {
    y.value = height;
    return true;
  }
  return false;
};

function getScrollTop2Index() {
  let totalHeight = 0;
  for (let i = 0; i < karaokeLineHeight.value.length; i++) {
    let h = karaokeLineHeight.value[i];
    if (i === 0) h /= 2;
    if (i === karaokeLineHeight.value.length - 1) h /= 2;
    totalHeight += h;
    if (totalHeight > y.value) {
      return i;
    }
  }
}

/**
 * 手动滚动后，自动对齐到当前行
 */
let autoAlignScrollTopTimer: number | undefined;
const autoAlignScrollTop = () => {
  if (!lrcParse.value) return;
  if (isScrolling.value) return;
  clearTimeout(autoAlignScrollTopTimer);
  autoAlignScrollTopTimer = setTimeout(() => {
    const scroll2Index = getScrollTop2Index();
    console.log("scroll2Index,", scroll2Index);
    if (scroll2Index !== void 0) {
      updateScrollTop(scroll2Index);
    }
    clearTimeout(autoAlignScrollTopTimer);
    autoAlignScrollTopTimer = void 0;
  }, 600);
};
const setDelyScaleLineShowFalse = useDebounceFn(() => {
  delyScaleLineShow.value = false;
}, delyScaleLineShowTime);
function onScrollStop() {
  // 自动滚动
  if (updateAutoActionScrolled.value) {
    updateAutoActionScrolled.value = false;
    // 手动滚动
  } else {
    delyScaleLineShow.value = true;
    setDelyScaleLineShowFalse();
    autoAlignScrollTop();
  }
}
/**
 * 重置当前索引
 */
function resetCurrentIndex() {
  currentIndex.value = -1;
  y.value = 0;
  updateAutoActionScrolled.value = true;
}

function handleClick() {
  console.log("click");
  emit("scaleLineClick", scaleLineCurrentTime.value);
}

watch(
  lrc,
  () => {
    if (!lrcParse.value) return;
    runner.setLrc(lrcParse.value);
  },
  { immediate: true }
);
watch(currentTime, updateCurrentIndex);
watch(
  currentIndex,
  (index) => {
    if (updateScrollTop(index)) {
      updateAutoActionScrolled.value = true;
    }
  },
  {
    immediate: true,
  }
);

defineExpose({
  resetCurrentIndex,
});
</script>
<style lang="scss" scoped>
.scale-line-wrap {
  width: v-bind(scaleLineWidth);
  position: fixed;
  left: v-bind(scaleLineLeft);
  top: v-bind(scaleLineWrapTop);
  z-index: 10;
}
.scale-line {
  background-color: v-bind(scaleLineColor);
  height: 1px;
}

.pla-top {
  width: 100%;
  height: calc(50%);
}
.pla-bot {
  width: 100%;
  height: calc(50%);
}
</style>
